perm filename WRIST.SAI[PNT,HE]6 blob
sn#404478 filedate 1978-12-18 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00008 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00002 00002 IFCR NOT DECLARATION($$PRGID) THENC
C00006 00003 STRING PROCEDURE GETTIM
C00008 00004 ⊃ procedure monitor(command,ppn) will log in a ptyjob with ppn and ex command
C00010 00005 ⊃ MATRIX SOLVERS: DECOMPOSE, SOLVE
C00017 00006 ⊃ MISC ROUTINES: SOLVER, TYPEFORCE
C00019 00007 ⊃ MATRIX INVERSION ROUTINES: INVERT, PINVERSE
C00022 00008 ⊃ START OF PROCEDURE THAT IS CALLED BY POINTY
C00032 ENDMK
C⊗;
IFCR NOT DECLARATION($$PRGID) THENC
ENTRY;
BEGIN "WRIST" ENDC
COMMENT - THIS PROGRAM IS USED TO CALIBRATE THE SCHEINMAN FORCE SENSING
WRIST.;
DEFINE ⊃="COMMENT",CR="'15",LF="'12",CRLF="('15&'12)",FF="'14";
DEFINE NSAMPS=10;
INTEGER I,J,K,DSET;
INTEGER DUM,CHAN,CCHAN,FLAG,ERR;
BOOLEAN TERSE,ASKAGAIN;
BOOLEAN ISCAL,DONTSTOP;
BOOLEAN SATURATED;
STRING COM1;
STRING ANS,MES,LINED;
STRING STOPIT,OUTBUF,OUTBUF2,OUTBUF3;
REAL DX,DY,DZ;
SAFE INTEGER ARRAY PS[1:50];
INTEGER ARRAY READINGS[1:NSAMPS,1:9];
INTEGER ARRAY IBASE[1:9];
REAL ARRAY AVER,CAVER,BASE,SD[1:9];
PRELOAD_WITH 1.0, 0.0, 0.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 1.0;
REAL ARRAY MPRIME[1:6,1:6];
PRELOAD_WITH
0.0, 0.0, 27.51, 0.0, 0.0, 0.0,
27.51, 0.0, 0.0, 0.0, 55.549, 0.0,
0.0, 27.51, 0.0, -55.549, 0.0, 0.0,
62.79, 0.0, 0.0, 0.0, 214.28, 0.0,
0.0, 62.79, 0.0, -214.28, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0, 0.0, 88.248;
OWN REAL ARRAY F[1:6,1:6];
PRELOAD_WITH
-124.0, -7.0, -1.8, 53.0, 115.6, -12.5, -8.0, -65.20,
20.0, 82.0, 134.7, -9.0, 14.0, -83.0, -111.0, 1.00,
-115.0, 8.00, 8.00, 791.0, 119.0, -21.0, -43.0, -789.20,
25.0, 409.0, 58.0, -19.1, 23.0, -398.5, -64.0, 15.1,
3.00, 39.10, 0.00, 35.00, -3.20, 45.00, 2.30, 47.00,
-265.0, 83.0, -138.90, 13.0, -255.20, -73.00, -396.00, -12.00;
OWN REAL ARRAY EPS[1:6,1:8];
REAL ARRAY M[1:6,1:8],MI[1:8,1:6];
EXTERNAL INTEGER PROCEDURE TLKEF6(INTEGER ARRAY READINGS);
REQUIRE "TLKEF6.REL[PNT,HE]" LOAD_MODULE;
PRESET_WITH
"NO CALIBRATION DATA FOR RESOLUTION",
"ROW VALUE MUST BE BETWEEN 1 THRU 6",
"ASKING TO RENAME FILE TO NULL";
INTERNAL STRING ARRAY $WRMSG[1:3];
INTEGER PROCEDURE UGETF(INTEGER CHAN);
BEGIN
define UGETF = '073000;
INTEGER I,CHN; LABEL DOUGTF;
CHN←CHAN;
quick_code;
move '13,CHN;
lsh '13,5;
addi '13,UGETF;
hrlm '13,DOUGTF; ⊃ PREPARE UGETF;
DOUGTF:
I ;
end;
RETURN(I);
END;
INTEGER CVAETAB;
PROCEDURE CVAET;
SETBREAK(CVAETAB←GETBREAK,"@",NULL,"IS");
REQUIRE CVAET INITIALIZATION;
STRING PROCEDURE CVAE(STRING MESS);
BEGIN
STRING S,S1;
INTEGER I;
S1←MESS;
S←NULL;
DO S←S&SCAN(S1,CVAETAB,I)&"E" UNTIL I=0;
RETURN(S[1 TO ∞ - 1]);
END;
STRING PROCEDURE GETTIM;
⊃ DETERMINES THE CURRENT DAY AND TIME, CONVERTS THEM TO ASC STRING
CONSTANTS AND RETURNS THE COMPOSITE STRING.;
BEGIN "GETTIM"
INTEGER DAY,HOUR,T,WID,DIG,YEAR,MON;
PRELOAD_WITH "JAN","FEB","MAR","APR","MAY","JUNE","JULY",
"AUG","SEPT","OCT","NOV","DEC";
OWN STRING ARRAY MONTHS[1:12];
STRING TIME;
⊃ GET THE CURRENT TIME;
GETFORMAT(WID,DIG);
SETFORMAT(-2,0);
TIME←"CURRENT TIME AND DATE: ";
QUICK_CODE
'47540400101;
HLRZ '14,'13;
HRRZ '13,'13;
MOVEM '13,HOUR;
MOVEM '14,DAY;
END;
⊃ COMPUTE AND CONVERT THE TIME OF DAY;
T←HOUR/60;
HOUR←T/60;
T←T-HOUR*60;
TIME←TIME&CVS(HOUR)&":"&CVS(T)&" ";
⊃ COMPUTE AND CONVERT THE DAY OF THE YEAR;
MON←DAY/31;
DAY←(DAY MOD 31)+1;
YEAR←(MON/12)+64;
MON←(MON MOD 12)+1;
TIME←TIME&CVS(DAY)&MONTHS[MON]&CVS(YEAR)&CRLF;
SETFORMAT(WID,DIG);
RETURN(TIME);
END "GETTIM";
⊃ procedure monitor(command,ppn) will log in a ptyjob with ppn and ex command;
SIMPLE INTEGER PROCEDURE LOGIN(STRING PPN(NULL));
BEGIN
STRING S;
external integer _skip_;
INTEGER PTYLINE;
PTYLINE←PTYGET;
ptyline←ptyget;
if _skip_=0 then outstr("couldn't get pty");
IF PPN≠NULL THEN S←PPN ELSE
BEGIN
STRING S1,S2;
S1←CVXSTR(CALL(0,"DSKPPN"))[1 TO 3];
S2←CVXSTR(CALL(0,"DSKPPN"))[4 TO 6];
WHILE S1=" " DO S1←S1[2 TO ∞];
WHILE S2=" " DO S2←S2[2 TO ∞];
S←S1&"."&S2;
END;
ptostr(PTYLINE,"L "&S&CRLF);
S←PTYSTR(PTYLINE,"↑");
S←PTYSTR(PTYLINE,".");
RETURN(PTYLINE);
END;
PROCEDURE MONCOM(INTEGER PTYLINE; STRING COMMAND);
BEGIN
STRING S;
PTOSTR(PTYLINE,COMMAND&CRLF);
S←PTYSTR(PTYLINE,"↑");
S←PTYSTR(PTYLINE,".");
END;
PROCEDURE LOGOUT(INTEGER PTYLINE);
PTYREL(PTYLINE);
PROCEDURE MONITOR(STRING COMMAND,PPN(NULL));
BEGIN
INTEGER PTY;
PTY←LOGIN(PPN);
MONCOM(PTY,COMMAND);
LOGOUT(PTY);
END;
⊃ MATRIX SOLVERS: DECOMPOSE, SOLVE;
PROCEDURE DECOMPOSE(INTEGER N;SAFE REAL ARRAY A,LU);
⊃ Both A and LU are [1:N, 1:N]. Uses global array PS. Computes
triangular matrices L and U and permutation matrix PS so that LU=PA.
Stores (L-I) and U both in LU. The call DECOMPOSE(N,A,A) will
overwrite A with LU. ;
BEGIN "decompose"
INTEGER I, J, K, PIVOTINDEX;
REAL NORMROW, PIVOT, SIZE, BIGGEST, MULT;
SAFE OWN REAL ARRAY R[1:50];
SIMPLE PROCEDURE ILOOP(INTEGER UL;REFERENCE REAL R1,R2);
⊃ Machine-coded for efficiency;
START_CODE
LABEL LP,EU;
MOVE 1,-1('17);
MOVE 2,-2('17);
MOVE 3,-3('17);
SUB 3,K;
JUMPLE 3,EU;
LP: AOJ 1,;
AOJ 2,;
MOVN 4,MULT;
FMPR 4,(1);
FADRM 4,(2);
SOJG 3,LP;
EU: END;
IF N > 50
THEN USERERR(0,1,"DECOMPOSE can't handle a matrix as large as" & CVS(N));
⊃ Initialize PS,LU and R;
FOR I←1 STEP 1 UNTIL N DO
BEGIN
PS[I]←I;
NORMROW←0;
FOR J←1 STEP 1 UNTIL N DO
BEGIN
LU[I,J]←A[I,J];
IF (NORMROW<ABS(LU[I,J])) THEN NORMROW←ABS(LU[I,J]);
END;
IF (NORMROW≠0)
THEN R[I]←1/NORMROW
ELSE BEGIN
R[I]←0;
USERERR(0,1,"Zero row in DECOMPOSE");
END;
END;
⊃ Gaussian elimination with partial pivoting;
FOR K←1 STEP 1 UNTIL N-1 DO
BEGIN "kloop";
BIGGEST ← 0;
FOR I ← K STEP 1 UNTIL N DO
BEGIN
SIZE←ABS(LU[PS[I],K])*R[PS[I]];
IF (BIGGEST<SIZE)
THEN BEGIN
BIGGEST←SIZE;
PIVOTINDEX←I;
END;
END;
IF BIGGEST = 0
THEN BEGIN
USERERR(0,1,"Singular matrix in DECOMPOSE");
DONE "kloop";
END;
IF PIVOTINDEX ≠ K
THEN BEGIN
J←PS[K];
PS[K]←PS[PIVOTINDEX];
PS[PIVOTINDEX]←J;
END;
PIVOT←LU[PS[K],K];
FOR I←K+1 STEP 1 UNTIL N DO
BEGIN
LU[PS[I],K]←MULT←(LU[PS[I],K]/PIVOT);
IF MULT ≠ 0
THEN ILOOP(N,LU[PS[I],K],LU[PS[K],K]);
⊃ The following is the result of the machine code:
FOR J ← K+1 STEP 1 UNTIL N DO
LU[PS[I],J]←LU[PS[I],J]-MULT*LU[PS[K],J];
END;
END "kloop";
IF (LU[PS[N],N]=0)
THEN USERERR(0,1,"Singular matrix in DECOMPOSE");
END "decompose";
SIMPLE PROCEDURE SOLVE(INTEGER N;SAFE REAL ARRAY LU,B,X);
⊃ Arrays LU[1:N,1:N], B[1:N], X[1:N]. Uses global safe integer array
PS. Solves AX=B using LU from DECOMPOSE. ;
BEGIN "solve"
INTEGER I,J;
REAL DOT;
SIMPLE PROCEDURE ILOOP(INTEGER LL,UL;REFERENCE REAL R1,R2);
⊃ Machine-coded for efficiency;
START_CODE
LABEL LP,EU;
MOVE 1,-1('17);
MOVE 2,-2('17);
MOVE 3,-3('17);
SUB 3,-4('17);
SETZ 4,;
JUMPL 3,EU;
LP: MOVE 5,(1);
FMPR 5,(2);
FADR 4,5;
AOJ 1,;
AOJ 2,;
SOJGE 3,LP;
EU: MOVEM 4,DOT;
END;
FOR I ← 1 STEP 1 UNTIL N DO
BEGIN
ILOOP(1,I-1,LU[PS[I],1],X[1]);
⊃ Has this effect:
DOT←0
FOR J←1 STEP 1 UNTIL I-1 DO
DOT←DOT+LU[PS[I],J]*X[J];
X[I]←B[PS[I]]-DOT;
END;
X[N] ← X[N] / LU[PS[N],N];
FOR I ← N-1 STEP -1 UNTIL 1 DO
BEGIN ⊃ RF: I changed loop upper index from N, to avoid
subscript errors;
ILOOP(I+1,N,LU[PS[I],I+1],X[I+1]);
⊃ Has this effect:
DOT←0
FOR J←I+1 STEP 1 UNTIL N DO
DOT←DOT+LU[PS[I],J]*X[J];
X[I]←(X[I]-DOT)/LU[PS[I],I];
END;
END "solve";
⊃ MISC ROUTINES: SOLVER, TYPEFORCE;
PROCEDURE SOLVER(REAL ARRAY MI,EPS,F);
BEGIN "SOLVER"
INTEGER I,J,K;
REAL ARRAY LU[1:6,1:6],E[1:6],M[1:6];
⊃ TRIANGULARIZE THE FORCE MATRIX;
DECOMPOSE(6,F,LU);
⊃ COPY THE SIX READINGS FOR EACH GAGE AND SOLVE FOR A
ROW OF THE INVERSE CALIBRATION MATRIX. REPEAT FOR
ALL EIGHT STRAIN GAGE PAIRS.;
FOR I ← 1 STEP 1 UNTIL 8 DO
BEGIN "SOLOOP"
FOR J ← 1 STEP 1 UNTIL 6 DO E[J]←EPS[J,I];
SOLVE(6,LU,E,M);
FOR J ← 1 STEP 1 UNTIL 6 DO MI[I,J]←M[J];
END "SOLOOP";
END "SOLVER";
PROCEDURE TYPEFORCE(REAL ARRAY F);
BEGIN "TYPEFORCE"
REAL MAG;
OUTSTR(CRLF&"THE RESULTING FORCE VECTOR IS ("&CVF(F[1])&
","&CVF(F[2])&","&CVF(F[3])&")"&CRLF&
"THE RESULTING MOMENT VECTOR IS("&CVF(F[4])&
","&CVF(F[5])&","&CVF(F[6])&")"&CRLF);
MAG← ( F[1]↑2 + F[2]↑2 + F[3]↑2 )↑0.5;
OUTSTR("THE MAGNITUDE OF THE FORCE IS "&CVF(MAG)&CRLF);
END "TYPEFORCE";
⊃ MATRIX INVERSION ROUTINES: INVERT, PINVERSE;
PROCEDURE INVERT (INTEGER N; REAL ARRAY A );
⊃ COMPUTES THE INVERSE OF THE NxN MATRIX "A" AND RETURNS THE INVERTED
MATRIX IN "A". THE PROCEDURES "SOLVE" AND "DECOMPOSE" ARE USED TO
COMPUTE THE INDIVIDUAL ROWS OF THE INVERSE MATRIX.;
BEGIN "INVERT"
INTEGER I,J;
REAL ARRAY LU[1:N,1:N],IDENT[1:N],X[1:N];
⊃ COPY THE ARRAY AND TRIANGULARIZE IT;
ARRTRAN(LU,A);
DECOMPOSE(N,LU,LU);
⊃ COMPUTE THE ROWS OF THE INVERSE ONE BY ONE;
FOR I ← 2 STEP 1 UNTIL N DO IDENT[I]←0.0;
FOR I ← 1 STEP 1 UNTIL N DO
BEGIN "INVLOOP"
IDENT[I]←1.0;
SOLVE(N,LU,IDENT,X);
FOR J ← 1 STEP 1 UNTIL N DO A[J,I]←X[J];
IDENT[I]←0.0;
END "INVLOOP";
END "INVERT";
PROCEDURE PINVERSE(REAL ARRAY M,MI);
⊃ COMPUTES THE PSUEDO INVERSE OF A NON-SQUARE 6x8 MATRIX, MI, AND
RETURNS THE INVERTED 8x6 MATRIX IN M. THE EQUATION IMPLEMENTED BY
THIS ROUTINE IS AS FOLLOWS:
T -1 T
M ← ( MI * MI ) * MI
WHERE THE "*" DENOTES MATRIX MULTIPLICATION;
BEGIN "PINVERSE"
REAL ARRAY A[1:6,1:6];
REAL STOTAL;
INTEGER I,J,K;
⊃ COMPUTE THE PRODUCT OF MI AND ITS TRANSPOSE;
FOR I ← 1 STEP 1 UNTIL 6 DO
FOR J ← 1 STEP 1 UNTIL 6 DO
BEGIN "PMULT"
STOTAL←0.0;
FOR K ← 1 STEP 1 UNTIL 8 DO
STOTAL←STOTAL+MI[K,I]*MI[K,J];
A[I,J]←STOTAL;
END "PMULT";
⊃ INVERT THE PRODUCT AND MULTIPLY BY THE TRANSPOSE AGAIN;
INVERT(6,A);
FOR I ← 1 STEP 1 UNTIL 6 DO
FOR J ← 1 STEP 1 UNTIL 8 DO
BEGIN "FMULT"
STOTAL←0.0;
FOR K ←1 STEP 1 UNTIL 6 DO
STOTAL←STOTAL+A[I,K]*MI[J,K];
M[I,J]←STOTAL;
END "FMULT";
END "PINVERSE";
⊃ START OF PROCEDURE THAT IS CALLED BY POINTY;
STRING FILE;
PROCEDURE INIT;
FILE←"FORCAL.CAL";
REQUIRE INIT INITIALIZATION;
INTERNAL INTEGER PROCEDURE RWRIST(STRING MODE; INTEGER WHICHROW(0);STRING FILENAME(NULL));
BEGIN INTEGER ERRCODE; ERRCODE←0;
IF EQU(MODE,"INIT")
THEN
BEGIN "INIT"
DX← DY← DZ ← 0.0;
TERSE←TRUE;
LINED←""; COM1←"";
⊃ READ IN THE CALIBRATION TABLE IF IT EXISTS, AND TYPE AN APPROPRIATE
MESSAGE.;
CCHAN←GETCHAN;
OPEN(CCHAN,"DSK",0,2,0,DUM,DUM,DUM);
LOOKUP(CCHAN,FILE,FLAG);
IF FLAG=0 THEN BEGIN
FOR I ← 1 STEP 1 UNTIL 6 DO
FOR J ←1 STEP 1 UNTIL 8 DO M[I,J]←REALIN(CCHAN);
OUTSTR("CALIBRATION TABLE READ FROM DISK"&CRLF);
ISCAL←TRUE;
END ELSE BEGIN
OUTSTR("NO CALIBRATION DATA FOUND ON DISK"&CRLF);
ISCAL←FALSE;
END;
RELEASE(CCHAN);
END "INIT"
ELSE IF EQU(MODE,"READ") THEN
BEGIN "READ"
INTEGER W,D;
SATURATED←FALSE;
DO ERR←TLKEF6(READINGS) UNTIL ERR=0;
⊃ COMPUTE STATISTICS FOR READINGS.;
FOR I←1 STEP 1 UNTIL 9 DO
BEGIN
AVER[I]←0.0;
SD[I]←0.0;
END;
FOR I←1 STEP 1 UNTIL NSAMPS DO
FOR J←1 STEP 1 UNTIL 9 DO
BEGIN
IF (J<9)∧(READINGS[I,J]<-1750.0 ∨ READINGS[I,J]>2000.0) THEN
SATURATED←TRUE;
AVER[J]←AVER[J]+READINGS[I,J];
SD[J]←SD[J]+READINGS[I,J]↑2;
END;
FOR I←9 STEP -1 UNTIL 1 DO
BEGIN
AVER[I]←AVER[I]/NSAMPS;
CAVER[I]←AVER[I]-BASE[I]-AVER[9]+BASE[9];
SD[I]←((SD[I]-NSAMPS*AVER[I]↑2)/(NSAMPS-1))↑0.5;
END;
GETFORMAT(W,D);
SETFORMAT(9,2);
IF SATURATED THEN OUTSTR(CRLF&"WRIST SENSOR READING OUT OF RANGE"&CRLF);
OUTBUF←GETTIM&
"Strain Gage Readings: Mean, Corrected Mean, Standard Dev."&
CRLF;
OUTBUF2←OUTBUF3←"";
FOR I ← 1 STEP 1 UNTIL 9 DO
BEGIN
OUTBUF←OUTBUF&CVF(AVER[I]);
OUTBUF2←OUTBUF2&CVF(CAVER[I]);
OUTBUF3←OUTBUF3&CVF(SD[I]);
END;
OUTBUF←OUTBUF&CRLF&OUTBUF2&CRLF&OUTBUF3&CRLF&CRLF;
SETFORMAT(W,D);
END "READ"
ELSE IF EQU(MODE,"BASE") THEN
BEGIN "BASE"
⊃ USER WANTS TO SET NEW DATA OFFSET;
FOR I←1 STEP 1 UNTIL 9 DO
BEGIN
BASE[I]←AVER[I];
IBASE[I]←READINGS[1,I];
END;
END "BASE"
⊃ FORCE AND MOMENT COMPUTATION;
ELSE IF EQU(MODE,"RESOLVE") THEN
BEGIN "RESOLVE"
IF ¬ISCAL THEN ERRCODE←1
ELSE BEGIN
REAL ARRAY F[1:6],FPRIME[1:6];
SETFORMAT(8,2);
FOR I←1 STEP 1 UNTIL 6 DO
BEGIN
F[I]←0.0;
FOR J←1 STEP 1 UNTIL 8 DO
F[I]←F[I]+M[I,J]*(READINGS[1,J]-IBASE[J]
-READINGS[1,9]+IBASE[9]);
END;
TYPEFORCE(F);
FOR I←1 STEP 1 UNTIL 6 DO
BEGIN
FPRIME[I]←0.0;
FOR J←1 STEP 1 UNTIL 6 DO
FPRIME[I]←FPRIME[I]+MPRIME[I,J]*F[J];
END;
OUTSTR(CRLF&"FORCE/MOMENTS RECOMPUTED AT ("&CVF(DX)&
","&CVF(DY)&","&CVF(DZ)&")"&CRLF);
TYPEFORCE(FPRIME);
ASKAGAIN←FALSE;
END
END "RESOLVE"
⊃ USE DATA FOR FORCE CALIBRATION, PRINT CURRENT DATA;
ELSE IF EQU(MODE,"CALIB") THEN
BEGIN "CALIB"
⊃ REPLACE OLD DATA WITH NEW;
IF 1≤WHICHROW≤ 6 THEN
FOR I ← 1 STEP 1 UNTIL 8 DO
EPS[WHICHROW,I]←CAVER[I]
ELSE ERRCODE←2;
END "CALIB"
⊃ ASK IF THE CALIBRATION MATRIX IS TO BE COMPUTED;
ELSE IF EQU(MODE,"COMPUTE") THEN
BEGIN "COMPUTE"
SOLVER(MI,EPS,F);
PINVERSE(M,MI);
ISCAL←TRUE;
END "COMPUTE"
⊃ SAVE NEW CALIBRATION ON THE DISK?;
ELSE IF EQU(MODE,"SAVECALIB") THEN
BEGIN "SAVECALIB"
CHAN←GETCHAN;
OPEN(CHAN,"DSK",0,0,2,120,DUM,DUM);
ENTER(CHAN,"FORCAL.CAL",DUM);
SETFORMAT(15,7);
FOR I←1 STEP 1 UNTIL 6 DO
BEGIN "PLINE"
MES←"";
FOR J ← 1 STEP 1 UNTIL 4 DO
MES←MES&CVE(M[I,J])&" ";
MES←MES&CRLF;
FOR J ← 5 STEP 1 UNTIL 8 DO
MES←MES&CVE(M[I,J])&" ";
MES←MES&CRLF;
OUT(CHAN,MES&CRLF);
END "PLINE";
OUT(CHAN,CRLF&CRLF&"CALIBRATION MATRIX: "&GETTIM);
RELEASE(CHAN);
END "SAVECALIB"
⊃ SAVE NEW CALIBRATION ON THE DISK?(SAVE TRANSPOSE,SUITABLE FOR PALX);
ELSE IF EQU(MODE,"SAVECALIBPAL") THEN
BEGIN "SAVECALIBPAL"
CHAN←GETCHAN;
OPEN(CHAN,"DSK",0,0,2,120,DUM,DUM);
ENTER(CHAN,"FORCAL.PAL",DUM);
MES←
".TITLE WRIST SENSOR CALIBRATION DATA TRANSPOSED
.OFFSET -320000 ;PUT IT IN THE DATA AREA
.=2000 ;START ADDRESS OF ARM DATA
";
OUT(CHAN,MES);
OUT(CHAN,CRLF&CRLF&";CALIBRATION MATRIX: "&GETTIM);
SETFORMAT(15,7);
FOR J←1 STEP 1 UNTIL 8 DO
BEGIN "PLINE"
MES←".FLT2 ";
FOR I ← 1 STEP 1 UNTIL 6 DO
MES←MES&CVE(M[I,J])&",";
MES←CVAE(MES);
OUT(CHAN,MES[1 to ∞ -1]&CRLF);
END "PLINE";
OUT(CHAN,".END"&CRLF);
RELEASE(CHAN);
END "SAVECALIBPAL"
ELSE IF EQU(MODE,"RENAMEFILE") THEN
BEGIN "RENAMEFILE"
IF FILENAME =NULL THEN ERRCODE←3;
FILE←FILENAME;
END "RENAMEFILE"
ELSE IF EQU(MODE,"SAVERAWDATA") THEN
BEGIN "SAVERAWDATA"
INTEGER CHAN;
CHAN←getchan;
OPEN(CHAN,"DSK",0,2,2,DUM,DUM,DUM);
LOOKUP(CHAN,"FORCAL.DAT",DUM);
ENTER(CHAN,"FORCAL.DAT",DUM);
UGETF(CHAN);
COMMENT FILENAME ACTUALLY IS THE COMMENT STRING;
OUT(CHAN,FILENAME&CRLF&OUTBUF&CRLF&FF);
RELEASE(CHAN);
END "SAVERAWDATA"
ELSE IF EQU(MODE,"DISPRAWDATA") THEN
OUTSTR(CRLF&OUTBUF&CRLF)
ELSE IF EQU(MODE,"COMPILEPALFILE") THEN
MONITOR("COMPILE FORCAL.PAL")
ELSE IF EQU(MODE,"COMPILEPALFILEONTOALHE") THEN
BEGIN
MONITOR("COMPILE FORCAL.PAL");
MONITOR("REN FORCAL.BIN[AL,HE]←FORCAL.BIN/Q");
END
ELSE OUTSTR(MODE& "is an unrecognized mode, choose one of the following"&crlf&
" INIT - initialize"&crlF&
" READ - read force sensors"&crlf&
" BASE - base readings"&CRLF&
" RESOLVE - resove forces and moments"&CRLF&
" CALIB - calibrate"&CRLF&
" SAVECALIB - save calibration data set on disk file"&crlf&
" SAVECALIBPAL-save palx version of transposed calibration data"&crlf&
" SAVERAWDATA - save wrist readings"&crlf&
" COMPILEPALFILE - compile FORCAL.PAL on current ppn"&crlf&
" COMPILEPALFILEONTOALHE - compile FORCAL.PAL onto AL,HE"&crlf&
" DISPRAWDATA - display wrist raw data"&crlf&
" RENAMEFILE - change force calibration data file"&CRLF);
RETURN(ERRCODE);
END;
END "WRIST"